// 7.3 类的其他特性
// 这些特性包括：类型成员、类的成员的类内初始值、可变数据成员、内联成员函数、从成员函数返回＊this
// 关于如何定义并使用类类型及友元类的更多知识。

// 7.3.1 类成员再探
/**
 * 为了展示这些新的特性，我们需要定义一对相互关联的类，它们分别是Screen和Window_mgr。
 *
 * 除了定义数据和函数成员之外，类还可以自定义某种类型在类中的别名。
 * 由类定义的类型名字和其他成员一样存在访问限制，可以是public或者private中的一种
 */

#include <iostream>
#include <vector>
using std::cerr, std::clog;
using std::cin, std::cout, std::endl;

/**
 * @brief Screen表示显示器中的一个窗口。
 * 每个Screen包含一个用于保存Screen内容的string成员和三个string：：size_type类型的成员，
 * 它们分别表示光标的位置以及屏幕的高和宽。
 */
class Screen
{
public:
    typedef std::string::size_type pos; // 用来定义类型的成员必须先定义后使用，这一点与普通成员有所区别，
    // 具体原因将在7.4.1节（第254页）解释。因此，类型成员通常出现在类开始的地方。
    // using pos = std::string::size_type; // 等价的声明pos
    Screen() = default; // 因为我们已经提供了一个构造函数，所以编译器将不会自动生成默认的构造函数。
                        // 如果我们的类需要默认构造函数，必须显式地把它声明出来。
    void some_member() const; // 常量成员函数 const成员函数

    // cursor被其类内初始值初始化为0
    Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c) {} // 构造函数
    char get() const { return contents[cursor]; }                                   // 隐式内联 读取光标处的字符
    inline char get(pos ht, pos wd) const;                                          // 显示内联
    Screen &move(pos r, pos c);                                                     // 能在之后被设为内联

private:
    mutable size_t access_crt; // 即使在const成员函数内也能被修改

private:
    pos cursor = 0;
    pos height = 0, width = 0;
    std::string contents;
};

// 我们可以在类的内部把inline作为声明的一部分显式地声明成员函数，
// 同样的，也能在类的外部用inline关键字修饰函数的定义：
// 虽然我们无须在声明和定义的地方同时说明inline，但这么做其实是合法的。
// 不过，最好只在类外部定义的地方说明inline，这样可以使类更容易理解。
inline Screen &Screen::move(pos r, pos c)
{
    pos row = r * width; // 计算行的位置
    cursor = row + c;    // 在行内将光标移动到指定的列
    return *this;        // 以左值的形式返回对象
}
char Screen::get(pos r, pos c) const
{
    pos row = r * width;      // 计算行的位置
    return contents[row + c]; // 返回给定列的字符
}

void Screen::some_member() const
{
    // 尽管some_member是一个const成员函数，但由于access_ctr是可变数据成员，所以some_member仍然能够改变的access_ctr值。
    ++access_crt; // 保存一个计数值，用于记录成员函数被调用的次数
}


// 类数据成员的初始值
// 默认情况下，我们希望Window_mgr类开始时总是拥有一个默认初始化的Screen。
// 在C++11新标准中，最好的方式就是把这个默认值声明成一个类内初始值
class Window_mgr
{
private:
    // 这个Window_mgr追踪的Screen
    // 默认情况下，一个Window_mgr包含一个标准尺寸的空白Screen
    std::vector<Screen> screens{Screen(24, 80, ' ')}; // 类数据成员的初始值，类内初始值
    // 如我们之前所知的，类内初始值必须使用=的初始化形式（初始化Screen的数据成员时所用的）
    // 或者花括号括起来的直接初始化形式（初始化screens所用的）。

    // 当我们提供一个类内初始值时，必须以符号=或者花括号表示。
};


int main()
{
    Screen myscreen;
    char ch = myscreen.get(); // 调用Screen::get()
    ch = myscreen.get(0, 0);  // 调用Screen::get(pos,pos)

    return 0;
}